package be.redo.model.entities;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

/**
 * @author Frank
 * 
 */
@Entity
@NamedQueries({
		@NamedQuery(name = "findFlightsByAirlineCompany", query = "select f from Flight f where f.airlineCompany = :airlineCompany"),
		@NamedQuery(name = "findFlightsByDepartureTimeBetween", query = "select f from Flight f where f.departureTime between :lowerTime and :higherTime"),
		@NamedQuery(name = "findFlightsByDepartureTimeEquals", query = "select f from Flight f where f.departureTime = :departureTime"),
		@NamedQuery(name = "findFlightsByLine", query = "select f from Flight f where f.line = :line"),
		@NamedQuery(name = "findFlightsByFlightCodeLike", query = "select f from Flight f where f.flightCode like( :flightCode )"),
		@NamedQuery(name = "findFlightsByLineWhereDepartureIsLaterThan", query = "select f from Flight f where f.line = :line and departureTime > :departureTime") })
public class Flight implements Serializable, Comparable<Flight> {

	/**
	 * Generated serialVersionUID.
	 */
	private static final long serialVersionUID = 5480538542498012238L;

	/**
	 * IATA Flight code. The first two character [A-Z] are the IATA code of the
	 * airline company. The next one to four numbers [0-9]{1,4} are the unique
	 * code for the airline company for one line. British Airways flight BA1 was
	 * the first concord flight in the morning to New York, every morning the
	 * was a new flight BA1.
	 */
	@NotNull
	@Pattern(regexp = "[A-Z]{2}[0-9]{1,4}")
	private String flightCode;

	/**
	 * Departure of the flight in local time. On creation the flight must be in
	 * the future.
	 * 
	 */
	@NotNull
	@Temporal(TemporalType.TIMESTAMP)
	@Future
	@DateTimeFormat(style = "M-")
	private Date departureTime;

	/**
	 * Duration of the flight in hours.
	 */
	@NotNull
	@Min(1)
	private int duration;

	/**
	 * number of free seats on the flight.
	 */
	@NotNull
	@Min(0)
	@Max(999)
	private int seats;

	/**
	 * Price of one seat in euros.
	 */
	@NotNull
	@Min(0)
	@NumberFormat(pattern = "#,##0.00")
	private BigDecimal price;

	/**
	 * Airline company carrying out this flight.
	 */
	@NotNull
	@ManyToOne
	@JoinColumn(name = "airline_company")
	private AirlineCompany airlineCompany;

	/**
	 * The line of this flight.
	 */
	@NotNull
	@ManyToOne
	@JoinColumn(name = "line")
	private Line line;

	@Override
	public String toString() {
		return ReflectionToStringBuilder.toString(this,
				ToStringStyle.SHORT_PREFIX_STYLE);
	}

	/**
	 * @return IATA FlightCode
	 */
	public String getFlightCode() {
		return this.flightCode;
	}

	/**
	 * 
	 * @param flightCode
	 *            IATA Flightcode
	 */
	public void setFlightCode(String flightCode) {
		this.flightCode = flightCode;
	}

	/**
	 * 
	 * @return Departure the time the flight will depart.
	 */
	public Date getDepartureTime() {
		return this.departureTime;
	}

	/**
	 * 
	 * @param departureTime
	 *            the time the plan will depart.
	 */
	public void setDepartureTime(Date departureTime) {
		this.departureTime = departureTime;
	}

	/**
	 * 
	 * @return the duration of the flight in hours.
	 */
	public int getDuration() {
		return this.duration;
	}

	/**
	 * 
	 * @param dureation
	 *            the duration of the flight in hours.
	 */
	public void setDuration(int dureation) {
		this.duration = dureation;
	}

	/**
	 * 
	 * @return the free seats for this flight.
	 */
	public int getSeats() {
		return this.seats;
	}

	/**
	 * 
	 * @param seats
	 *            the free seats for this flight.
	 */
	public void setSeats(int seats) {
		this.seats = seats;
	}

	/**
	 * 
	 * @return the price of the flight in euros.
	 */
	public BigDecimal getPrice() {
		return this.price;
	}

	/**
	 * 
	 * @param price
	 *            the price of the flight in euros.
	 */
	public void setPrice(BigDecimal price) {
		this.price = price;
	}

	/**
	 * 
	 * @return the airline company carry out this flight.
	 */
	public AirlineCompany getAirlineCompany() {
		return this.airlineCompany;
	}

	/**
	 * 
	 * @param airlineCompany
	 *            the airline company carry out this flight.
	 */
	public void setAirlineCompany(AirlineCompany airlineCompany) {
		this.airlineCompany = airlineCompany;
	}

	/**
	 * 
	 * @return the line (departure and destination airports).
	 */
	public Line getLine() {
		return this.line;
	}

	/**
	 * 
	 * @param line
	 *            the line (departure and destination airports).
	 */
	public void setLine(Line line) {
		this.line = line;
	}

	/**
	 * Id generated by the database.
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "flight_id")
	private Long FlightId;

	/**
	 * 
	 * @return the by the database generated ID.
	 */
	public Long getFlightId() {
		return this.FlightId;
	}

	/**
	 * 
	 * @param id
	 *            the by the database generated ID.
	 */
	public void setFlightId(Long id) {
		this.FlightId = id;
	}

	/**
	 * compares two flights by price
	 */
	@Override
	public int compareTo(Flight o) {
		return this.price.compareTo(o.price);
	}

	/**
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((departureTime == null) ? 0 : departureTime.hashCode());
		result = prime * result
				+ ((flightCode == null) ? 0 : flightCode.hashCode());
		return result;
	}

	/**
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		Flight other = (Flight) obj;
		if (departureTime == null) {
			if (other.departureTime != null) {
				return false;
			}
		} else if (!departureTime.equals(other.departureTime)) {
			return false;
		}
		if (flightCode == null) {
			if (other.flightCode != null) {
				return false;
			}
		} else if (!flightCode.equals(other.flightCode)) {
			return false;
		}
		return true;
	}

	public static Comparator<Flight> getPriceComparator() {
		return new Comparator<Flight>() {

			@Override
			public int compare(Flight f1, Flight f2) {
				return f1.getPrice().compareTo(f2.getPrice());
			}
		};
	}

}
